﻿// PasswordHasherTest.cs
// 
// Copyright (C) 2008-2009 Scott Wegner
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using PwdhashSharp.Library;
using System.Diagnostics;

namespace PwdhashSharp.Library.Tests
{
    [TestClass]
    public class PasswordHasherTest
    {
        #region Test Strings
        protected readonly string[][] ShortPasswordTests = {
			new string []{"domain.com", "a", "Oq0M"},
			new string []{"domain.com", "aa", "o0SA"},
			new string []{"domain.com", "ab", "HPl0"},
			new string []{"domain.com", "6", "l1UM"}
		};

        protected readonly string[][] ShortSymbolPasswordTests = {
			new string []{"domain.com", "*", "w7+V"},
			new string []{"domain.com", "!", "+Yr3"},
			new string []{"domain.com", ",", "0+Vf"},
			new string []{"domain.com", " ", "4+Sr"},
			new string []{"domain.com", "a.", "8+Fy"},
			new string []{"domain.com", "b!", "+Xb0"},
			new string []{"domain.com", "()", "f8+C"},
			new string []{"domain.com", "v^", "k0+S"},
			new string []{"domain.com", " !", "c6+I"},
			new string []{"domain.com", " a", "k9+D"}
		};

        protected readonly string[][] LongPasswordTests = {
			new string []{"domain.com", "ThisIsAVeryLongPassword", "IZw00vyBimO4MiRxKiy5OgAAA"},
			new string []{"domain.com", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "PP68Qj6JaGyBdAsq1LhaPgAAAA"},
			new string []{"domain.com", "ThisIsAnEvenLongerPasswordWithTheNumber1InIt", "NFyl7CLA2DlxOIUosBTCSQAAAA"},
			new string []{"domain.com", "thisMightBeTheLongestNonSymbolPasswordYouEverEncounterInFactItIsAlmostANovelInTheLengthOfItTheCurrentLengthIs140ishButImNotStoppingThereIThinkItWouldBeGoodToTestAPasswordThatIsAtLeast300CharactersLongSoThatIsTheGoalOfThisPasswordPwdhashSharpShouldBeAbleToHandleStringsOfAnyLengthLimitedOnlyByTheParsingAbilityOfCSharpSoThisShouldBeOK", "dld2GNONhZCZUMSXUsbKMwAAAA"},
		};

        protected readonly string[][] LongSymbolPasswordTests = {
			new string []{"domain.com", "ThisIsAVeryLongPasswordWith ASpaceInIt", "FbeZqBylQBKnbo/iz6yNHA"},
			new string []{"domain.com", "!(*&(*@&$(@#(&#*(%&(*#&@(&$@(%&(*#%&#(*@)#*(#%&", "FhNYB9jk067L+uTx7nOdUA"},
			new string []{"domain.com", "                                                            abc", "bCDkZgXYNlIdyaqDRmBSpw"},
			new string []{"domain.com", "thisMightBeTheLongestNonSymbolPasswordYouEverEncounterInFactItIsAlmostANovelInTheLengthOfItTheCurrentLengthIs140ishButImNotStoppingThereIThinkItWouldBeGoodToTestAPasswordThatIsAtLeast300CharactersLongSoThatIsTheGoalOfThisPasswordPwdhashSharpShouldBeAbleToHandleStringsOfAnyLengthLimitedOnlyByTheParsingAbilityOfCSharpSoThisShouldBeOK!", "yxjFlrBh6sgScDLMSWH95g"}
		};

        protected readonly string[][] ShortDomainTests = {
			new string []{"a.a", "password", "ZVjnnP5Myn"},
			new string []{"b.com", "password", "wTgUE4CeUs"},
			new string []{"b.c", "password", "7xdO1e5hZc"},
			new string []{"d.co", "password", "qF6LbEiySp"}
		};

        protected readonly string[][] ShortDomainSymbolTests = {
			new string []{"a.a", "password!", "GIplIepGL+4"},
			new string []{"b.com", "password!", "N70YkL+2vwi"},
			new string []{"b.c", "password!", "sqqXL7+DiJI"},
			new string []{"d.co", "password!", "iV+arI4cnOn"}
		};

        protected readonly string[][] LongDomainTests = {
			new string []{"thisisaprettylongdomainname.com", "password", "5JvCcaHHVS"},
			new string []{"thisIsAnEvenLongerDomainNameAndItContainsTheNumber1InIt.com", "password", "bn1J4JeH7N"},
			new string []{"thisDomain.containsAVeryLongSuffixAlthoughTheDifferenceShouldntMatter", "password", "7jsQ5CtM8E"},
			new string []{"thisIsASignificantlyLongDomainNameIFeelLikeWeShouldTrySomethingThatIsAtLeast300CharactersAsWeHaveDoneWIthThePasswordTestsTheDifferenceInLengthShouldntMatter.AtAllAsItIsAllHashedEquallyHoweverThisIsToAssureThatNoBufferOverrunsOccurAtAnyPointItShouldntInCSharpBecauseNoPointersAreManipulatedDirectlyHoweverThisCouldAlsoPotentiallyCatchMemoryCorruption", "password", "yKGIbWH9PJ"},
		};

        protected readonly string[][] LongDomainSymbolTests = {
			new string []{"thisisaprettylongdomainname.com", "password!", "4j4op+vspYy"},
			new string []{"thisIsAnEvenLongerDomainNameAndItContainsTheNumber1InIt.com", "password!", "fS+F1B9jajn"},
			new string []{"thisDomain.containsAVeryLongSuffixAlthoughTheDifferenceShouldntMatter", "password!", "1DsxRfzNIf+"},
			new string []{"thisIsASignificantlyLongDomainNameIFeelLikeWeShouldTrySomethingThatIsAtLeast300CharactersAsWeHaveDoneWIthThePasswordTestsTheDifferenceInLengthShouldntMatter.AtAllAsItIsAllHashedEquallyHoweverThisIsToAssureThatNoBufferOverrunsOccurAtAnyPointItShouldntInCSharpBecauseNoPointersAreManipulatedDirectlyHoweverThisCouldAlsoPotentiallyCatchMemoryCorruption", "password!", "6uA+2tErQeG"},
		};

        protected readonly string[][] MultipartDomainTests = {
           new string []{"one", "password", "iwBQIG7CSc"},
           new string []{"two.parts", "password", "kFOM8PPlOR"},
           new string []{"this.has.three", "password", "L0tKmyC0Lo"},
           new string []{"this.has.four.parts","password", "VwWydkkkX8"}
       };

        protected readonly string[][] NonDomainTests = {
			new string []{"thisisnotadomain", "password", "8WWEaFlNfG"},
			new string []{"thisisthe2ndnondomain", "password", "1PbFqzxZNb"},
			new string []{"totallynot", "password", "CeNYNXJw4Y"},
			new string []{"nopenotadomain", "password", "7LFvRO2tFY"}
		};

        protected readonly string[][] NonDomainSymbolTests = {
			new string []{"thisisnotadomain", "password!", "xKrV+8BJvRs"},
			new string []{"thisisalsonotadmoain", "password!", "MG1+NNLcFUV"},
			new string []{"totallynot", "password!", "6s11cGIM5+x"},
			new string []{"nopenotadomain", "password!", "g/Lbgs7YhkK"}
		};

        protected readonly string[][] ShortShortTests = {
			new string []{"a.a", "a", "m4GC"},
			new string []{"b.com", "aa", "g9LZ"},
			new string []{"b.c", "ab", "Xu5L"},
			new string []{"d.co", "6", "i4AG"}
		};

        protected readonly string[][] ShortShortSymbolTests = {
			new string []{"a.a", "*", "Rb0+"},
			new string []{"b.com", "!", "+Ig7"},
			new string []{"b.c", ",", "+Sq7"},
			new string []{"d.co", " ", "b7+B"},
			new string []{"a.a", "a.", "v8+M"},
			new string []{"b.com", "b!", "7+Wb"},
			new string []{"b.c", "()", "5+Fr"},
			new string []{"d.co", "v^", "+Fy2"},
			new string []{"a.a", " !", "Pz7+"},
			new string []{"b.com", " a", "+Jp8"}
		};

        protected readonly string[][] LongLongTests = {
			new string []{"thisisaprettylongdomainname.com", "ThisIsAVeryLongPassword", "yJJBX8E6QFmkgtwWPvqSiwAAA"},
			new string []{"thisIsAnEvenLongerDomainNameAndItContainsTheNumber1InIt.com", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "7eOqCuJjqCYvTWjDPynS9wAAAA"},
			new string []{"thisDomain.containsAVeryLongSuffixAlthoughTheDifferenceShouldntMatter", "ThisIsAnEvenLongerPasswordWithTheNumber1InIt", "o0Nxc7r6x76d6Oxt0vHxnQAAAA"},
			new string []{"thisIsASignificantlyLongDomainNameIFeelLikeWeShouldTrySomethingThatIsAtLeast300CharactersAsWeHaveDoneWIthThePasswordTestsTheDifferenceInLengthShouldntMatter.AtAllAsItIsAllHashedEquallyHoweverThisIsToAssureThatNoBufferOverrunsOccurAtAnyPointItShouldntInCSharpBecauseNoPointersAreManipulatedDirectlyHoweverThisCouldAlsoPotentiallyCatchMemoryCorruption", "thisMightBeTheLongestNonSymbolPasswordYouEverEncounterInFactItIsAlmostANovelInTheLengthOfItTheCurrentLengthIs140ishButImNotStoppingThereIThinkItWouldBeGoodToTestAPasswordThatIsAtLeast300CharactersLongSoThatIsTheGoalOfThisPasswordPwdhashSharpShouldBeAbleToHandleStringsOfAnyLengthLimitedOnlyByTheParsingAbilityOfCSharpSoThisShouldBeOK", "NerZuu1Ydm7AVmAFPRApzQAAAA"},
		};

        protected readonly string[][] LongLongSymbolTests = {
			new string []{"thisisaprettylongdomainname.com", "ThisIsAVeryLongPasswordWith ASpaceInIt", "f+6EL6cWlKXKYhU1RyagpQ"},
			new string []{"thisIsAnEvenLongerDomainNameAndItContainsTheNumber1InIt.com", "!(*&(*@&$(@#(&#*(%&(*#&@(&$@(%&(*#%&#(*@)#*(#%&", "tVfwPTsK5LNRHqFyzjAn+w"},
			new string []{"thisDomain.containsAVeryLongSuffixAlthoughTheDifferenceShouldntMatter", "                                                            abc", "w1Lb2CkO3rkEYiIxX88lDg"},
			new string []{"thisIsASignificantlyLongDomainNameIFeelLikeWeShouldTrySomethingThatIsAtLeast300CharactersAsWeHaveDoneWIthThePasswordTestsTheDifferenceInLengthShouldntMatter.AtAllAsItIsAllHashedEquallyHoweverThisIsToAssureThatNoBufferOverrunsOccurAtAnyPointItShouldntInCSharpBecauseNoPointersAreManipulatedDirectlyHoweverThisCouldAlsoPotentiallyCatchMemoryCorruption", "thisMightBeTheLongestNonSymbolPasswordYouEverEncounterInFactItIsAlmostANovelInTheLengthOfItTheCurrentLengthIs140ishButImNotStoppingThereIThinkItWouldBeGoodToTestAPasswordThatIsAtLeast300CharactersLongSoThatIsTheGoalOfThisPasswordPwdhashSharpShouldBeAbleToHandleStringsOfAnyLengthLimitedOnlyByTheParsingAbilityOfCSharpSoThisShouldBeOK!", "njZP6uO5Vacq86SqVaF8Dg"}
		};

        #endregion

        #region Test Cases
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestNullDomain()
        {
            new PasswordHasher(null, "password").CreateHashedPassword();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestEmptyDomain()
        {
            new PasswordHasher(string.Empty, "password").CreateHashedPassword();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestNullPassphrase()
        {
            new PasswordHasher("domain.com", null).CreateHashedPassword();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestEmptyPassphrase()
        {
            new PasswordHasher("domain.com", string.Empty).CreateHashedPassword();
        }

        [TestMethod]
        public void TestShortPasswords() { TestValid(ShortPasswordTests); }

        [TestMethod]
        public void TestShortSymbolPasswords() { TestValid(ShortSymbolPasswordTests); }

        [TestMethod]
        public void TestLongPasswords() { TestValid(LongPasswordTests); }

        [TestMethod]
        public void TestLongSymbolPasswords() { TestValid(LongSymbolPasswordTests); }

        [TestMethod]
        public void TestShortDomains() { TestValid(ShortDomainTests); }

        [TestMethod]
        public void TestShortSymbolDomains() { TestValid(ShortDomainSymbolTests); }

        [TestMethod]
        public void TestLongDomains() { TestValid(LongDomainTests); }

        [TestMethod]
        public void TestMultipartDomains() { TestValid(MultipartDomainTests); }

        [TestMethod]
        public void TestLongSymbolDomains() { TestValid(LongDomainSymbolTests); }

        [TestMethod]
        public void TestNonDomains() { TestValid(NonDomainTests); }

        [TestMethod]
        public void TestNonDomainSymbols() { TestValid(NonDomainSymbolTests); }

        [TestMethod]
        public void TestShortShorts() { TestValid(ShortShortTests); }

        [TestMethod]
        public void TestShortShortSymbols() { TestValid(ShortShortSymbolTests); }

        [TestMethod]
        public void TestLongLongs() { TestValid(LongLongTests); }

        [TestMethod]
        public void TestLongLongSymbols() { TestValid(LongLongSymbolTests); }

        #endregion

        #region Helper Methods

        /// <summary>
        /// For each string pair, check that the generated output matches the expected
        /// </summary>
        /// <param name="validUrls">2D array of strings, with the first cell being the input, and second being the output</param>
        public static void TestValid(string[][] validPairs)
        {
            Debug.Assert(validPairs != null);
            foreach (string[] pair in validPairs)
            //for (int i = 0; i < validPairs.Length / 3; i++)
            {
                Debug.Assert(pair.Length >= 3);

                PasswordHasher ph = new PasswordHasher(pair[0],
                                                       pair[1]);
                Assert.AreEqual(
                           pair[2],
                            ph.CreateHashedPassword(),
                            "Testing " + pair[0] + ", " + pair[1]);
            }
        }

        #endregion
    }
}
